package com.ruoyi.activiti.controller;

import cn.hutool.core.collection.CollectionUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ruoyi.activiti.domain.MyProcessDefinition;
import com.ruoyi.activiti.domain.ProcessStarter;
import com.ruoyi.activiti.service.ActivitiConvertService;
import com.ruoyi.activiti.service.ProcessDefinitionService;
import com.ruoyi.activiti.util.ProcessCanstant;
import com.ruoyi.common.core.utils.SecurityUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.poi.ExcelUtil;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.AjaxPageResult;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.UserTask;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author scihi
 */
@RestController
@RequestMapping("/activiti/definition")
@Api(tags="流程定义")
@Validated//有此注解，才会校验游离的参数
public class ProcessDefinitionController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(ProcessDefinitionController.class);

    @Autowired
    private ProcessDefinitionService processDefinitionService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private IdentityService identityService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private ActivitiConvertService activitiConvertService;

    @ApiOperation(value = "查询流程定义列表", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "流程定义名字，模糊匹配", paramType = "form")
            , @ApiImplicitParam(name = "key", value = "流程定义key", paramType = "form")
            , @ApiImplicitParam(name = "category", value = "流程分组", paramType = "form")
    })
    @GetMapping("/list")
    public AjaxPageResult list(String category, String name, String key) {
        MyProcessDefinition processDefinition = new MyProcessDefinition();
        processDefinition.setCategory(category);
        processDefinition.setName(name);
        processDefinition.setKey(key);
        List<MyProcessDefinition> list = processDefinitionService.listProcessDefinition(processDefinition);
        return AjaxPageResult.success(list);
    }

    @ApiOperation(value = "查询流程定义", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "procDefId", value = "流程定义id", paramType = "path")
    })
    @GetMapping("/{procDefId}")
    public AjaxResult list(@PathVariable String procDefId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(procDefId)
                .singleResult();
        return AjaxResult.success(activitiConvertService.convertProcessDefinition(processDefinition, true));
    }

    @ApiOperation(value = "导出流程定义excel", notes = "")
    @ApiImplicitParams({})
    @Log(title = "流程定义", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public AjaxResult export(HttpServletResponse response) throws IOException {
        List<MyProcessDefinition> list = processDefinitionService.listProcessDefinition(new MyProcessDefinition());
        ExcelUtil<MyProcessDefinition> util = new ExcelUtil<>(MyProcessDefinition.class);
        util.exportExcel(response, list, "流程定义数据");
        return AjaxResult.success();
    }

    @ApiOperation(value = "删除流程定义", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deploymentId", value = "流程部署id，流程定义详情中有", paramType = "path")
    })
    @Log(title = "流程定义", businessType = BusinessType.DELETE)
    @DeleteMapping("/remove/{deploymentId}")
    public AjaxResult remove(@PathVariable String deploymentId) {
        try {
            return AjaxResult.success(processDefinitionService.deleteProcessDeploymentByIds(deploymentId));
        }
        catch (Exception e) {
            return error(e.getMessage());
        }
    }

    @ApiOperation(value = "停用/恢复流程定义", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "procDefId", value = "流程定义id", paramType = "form")
            , @ApiImplicitParam(name = "suspendState", value = "状态，1：停用，2：恢复", paramType = "form")
    })
    @PostMapping( "/suspendOrActiveDefinition")
    public AjaxResult suspendOrActiveDefinition(String procDefId, String suspendState) {
        processDefinitionService.suspendOrActiveDefinition(procDefId, suspendState);
        return success();
    }

    @ApiOperation(value = "查看流程图", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "procDefId", value = "流程定义id", paramType = "path")
    })
    @GetMapping(value = "/diagram/{procDefId}")
    public void diagram(@PathVariable("procDefId") String procDefId, HttpServletResponse response) throws Exception {
        // 设置页面不缓存
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        /*ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(procDefId)
                .singleResult();
        if(StringUtils.isNull(procDef)){
            throw new Exception("流程定义不存在");
        }*/

        //InputStream resourceAsStream = repositoryService.getResourceAsStream(procDef.getDeploymentId(), procDef.getDiagramResourceName());
        InputStream resourceAsStream = repositoryService.getProcessDiagram(procDefId);
        byte[] b = new byte[1024];
        int len = -1;
        while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
            response.getOutputStream().write(b, 0, len);
        }
    }

    @ApiOperation(value = "启动流程", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "key", value = "流程定义key", paramType = "path")
            ,@ApiImplicitParam(name = "processStarter", value = "启动参数", paramType = "body")
    })
    @PostMapping(value = "/start/{key}")
    public AjaxResult start(@PathVariable("key") String key, @RequestBody ProcessStarter processStarter) {
        String username = StringUtils.isNotEmpty(processStarter.getCreateBy())? processStarter.getCreateBy(): SecurityUtils.getUsername();
        identityService.setAuthenticatedUserId(username);
        //记录流程变量
        processStarter.getVariables().put(ProcessCanstant.CREATE_BY, username);
        ProcessInstanceBuilder builder = runtimeService.createProcessInstanceBuilder();
        ProcessInstance processInstance = builder.processDefinitionKey(key)
                .businessKey(processStarter.getBusinessKey())//
                .name(processStarter.getTitle())//流程实例标题
                .variables(processStarter.getVariables())
                .start();
        HistoricProcessInstance hist = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstance.getId())
                .singleResult();
        return AjaxResult.success(activitiConvertService.convertProcessInstance(hist, true));
    }

    @ApiOperation(value = "查询流程图节点列表", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "procDefId", value = "流程定义id", paramType = "path")
    })
    @PostMapping(value = "/activities/{procDefId}")
    public AjaxResult activities(@PathVariable("procDefId") String procDefId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);
        Process process = bpmnModel.getProcesses().get(0);
        List<UserTask> tasks = process.findFlowElementsOfType(UserTask.class);
        if(CollectionUtil.isEmpty(tasks)){
            return AjaxResult.error("无数据");
        }
        List<UserTask> newList = new ArrayList<>();
        tasks.forEach(task->{//简化输出结果
            UserTask userTask = new UserTask();
            userTask.setName(task.getName());
            userTask.setId(task.getId());
            userTask.setFormKey("此处可保存表单路径");
            newList.add(userTask);
        });
        return AjaxResult.success(newList);
    }

    @ApiOperation(value = "转化为流程图文件", notes = "")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "processDefinitionId", value = "流程部署id，流程定义详情中有", paramType = "form")
    })
    @PostMapping(value = "/convert2Model")
    public AjaxResult convertToModel(String processDefinitionId) throws XMLStreamException {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId).singleResult();
        InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                processDefinition.getResourceName());
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = new InputStreamReader(bpmnStream, StandardCharsets.UTF_8);
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        BpmnJsonConverter converter = new BpmnJsonConverter();
        ObjectNode modelNode = converter.convertToJson(bpmnModel);
        Model modelData = repositoryService.newModel();
        modelData.setKey(processDefinition.getKey());
        modelData.setName(processDefinition.getResourceName());
        modelData.setCategory(processDefinition.getDeploymentId());

        ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
        modelData.setMetaInfo(modelObjectNode.toString());

        repositoryService.saveModel(modelData);
        repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes(StandardCharsets.UTF_8));

        return AjaxResult.success();
    }
}
